import os
from pathlib import Path
import tweepy
import time
import datetime
from google import genai
from google.genai import types
from typing import List, Dict, Any, Optional
import json


def fetch_recent_posts(
    username: str,
    bearer_token: str,
    lookback_hours: int = 24,
    include_replies: bool = False,
    include_retweets: bool = False,
    limit: Optional[int] = 100
) -> List[Dict[str, Any]]:
    """
    从 X (Twitter) API v2 获取单个指定用户的近期推文。

    Args:
        username (str): 目标用户的 X handle (不带 '@')。
        bearer_token (str): 你的 X API v2 Bearer Token。
        lookback_hours (int, optional): 回溯的小时数。默认为 24。
        include_replies (bool, optional): 是否包含回复。默认为 False。
        include_retweets (bool, optional): 是否包含转推。默认为 False。
        limit (Optional[int], optional): 返回推文的最大数量。
                                         API单次请求上限为100。默认为 100。

    Returns:
        List[Dict[str, Any]]: 一个包含推文信息的列表。
                              每个字典包含: 'id', 'text', 'created_at', 'author_username'。
                              如果用户不存在或没有发布推文，则返回空列表。
    """
    print("--- 初始化 Tweepy 客户端 ---")
    try:
        # 使用 Bearer Token 初始化 Tweepy Client
        # client = tweepy.Client(bearer_token)
        client = tweepy.Client(bearer_token, wait_on_rate_limit=True)
    except Exception as e:
        print(f"错误：无法初始化 Tweepy 客户端: {e}")
        return []

    print(f"--- 正在获取用户 '{username}' 的 ID ---")
    try:
        # API v2 需要用户 ID 而不是用户名来获取推文
        user_response = client.get_user(username=username)
        if user_response.data is None:
            print(f"错误：找不到用户 '{username}'。")
            return []
        user_id = user_response.data.id
        author_username = user_response.data.username # 保存准确的用户名
        print(f"成功获取用户 ID: {user_id}")
    except tweepy.errors.TweepyException as e:
        print(f"错误：获取用户ID时发生 API 错误: {e}")
        return []

    # 计算查询的开始时间 (UTC)
    start_time = datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(hours=lookback_hours)

    # 构建排除规则
    exclude_rules = []
    if not include_replies:
        exclude_rules.append('replies')
    if not include_retweets:
        exclude_rules.append('retweets')

    print(f"--- 正在获取用户 {username} 在过去 {lookback_hours} 小时内的推文 ---")
    print(f"排除规则: {exclude_rules or '无'}")
    
    formatted_tweets = []
    
    try:
        # 使用 Paginator 处理可能跨越多页的请求
        # 注意：免费版API的速率限制非常严格，Paginator在请求多页时可能会很快达到上限
        paginator = tweepy.Paginator(
            client.get_users_tweets,
            id=user_id,
            start_time=start_time,
            exclude=exclude_rules or None,
            tweet_fields=["id", "text", "created_at"],
            max_results=min(100, limit) if limit else 100 # API单次请求最多100条
        )

        # 遍历推文并格式化
        for response in paginator:
            # 检查响应中是否有数据
            if response.data:
                for tweet in response.data:
                    formatted_tweets.append({
                        "id": str(tweet.id),
                        "text": tweet.text,
                        "created_at": tweet.created_at.isoformat(),
                        "author_username": author_username
                    })
                    # 如果达到了用户设定的上限，则停止获取
                    if limit and len(formatted_tweets) >= limit:
                        break
            if limit and len(formatted_tweets) >= limit:
                break
                
    except tweepy.errors.TweepyException as e:
        print(f"错误：获取推文时发生 API 错误: {e}")
        # 即使出错，也返回已经获取到的部分
        return formatted_tweets
    except Exception as e:
        print(f"发生未知错误: {e}")
        return formatted_tweets

    print(f"--- 成功获取 {len(formatted_tweets)} 条推文 ---")
    return formatted_tweets


def fetch_all_leaders_posts(
    leader_usernames: List[str],
    bearer_token: str,
    lookback_hours: int = 24,
    include_replies: bool = False,
    include_retweets: bool = False,
    limit_per_user: Optional[int] = 100
) -> List[Dict[str, Any]]:
    """
    遍历一个用户名列表，调用 fetch_recent_posts 函数来收集所有人的推文。

    Args:
        leader_usernames (List[str]): 要获取推文的 X 用户名列表。
        bearer_token (str): 你的 X API v2 Bearer Token。
        lookback_hours (int, optional): 回溯的小时数。默认为 24。
        include_replies (bool, optional): 是否包含回复。默认为 False。
        include_retweets (bool, optional): 是否包含转推。默认为 False。
        limit_per_user (Optional[int], optional): 对每个用户获取推文的最大数量。默认为 100。

    Returns:
        List[Dict[str, Any]]: 一个整合了所有目标用户推文的单一列表。
    """
    all_tweets = []
    print(f"\n=== 开始为 {len(leader_usernames)} 位领导人批量获取推文 ===")
    
    for i, username in enumerate(leader_usernames):
        print(f"\n[{i+1}/{len(leader_usernames)}] ---> 正在处理用户: {username}")
        
        # 调用之前定义的函数来获取单个用户的推文
        tweets = fetch_recent_posts(
            username=username,
            bearer_token=bearer_token,
            lookback_hours=lookback_hours,
            include_replies=include_replies,
            include_retweets=include_retweets,
            limit=limit_per_user
        )
        
        if tweets:
            all_tweets.extend(tweets)
            
        # 在两次请求之间短暂暂停一下，避免触发过于频繁的请求限制
        if i < len(leader_usernames) - 1:
             print("---> 处理完毕，暂停 1 秒...")
             time.sleep(1)

    print(f"\n=== 批量获取完成。总共收集到 {len(all_tweets)} 条推文 ===")
    return all_tweets


def save_tweets_to_file(
    tweets_data: List[Dict[str, Any]],
    output_dir: str = '../data'
) -> str:
    """
    将获取到的推文数据列表保存到带有时间戳的 JSON 文件中。

    Args:
        tweets_data (List[Dict[str, Any]]): 包含推文信息的列表。
        output_dir (str, optional): 用于存放输出文件的目录名。
                                    默认为 'data'。如果目录不存在，会自动创建。

    Returns:
        str: 成功保存的文件路径。如果保存失败，则返回空字符串。
    """
    if not tweets_data:
        print("--- 数据为空，无需保存文件。 ---")
        return ""

    try:
        # 步骤 1: 确保输出目录存在
        os.makedirs(output_dir, exist_ok=True)

        # 步骤 2: 生成带时间戳的文件名
        # 使用当前时间保证文件名唯一
        timestamp = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
        filename = f"tweets_{timestamp}.json"
        
        # 步骤 3: 构造完整的文件路径
        file_path = os.path.join(Path(output_dir), filename)

        # 步骤 4: 将数据写入 JSON 文件
        print(f"\n--- 正在将 {len(tweets_data)} 条推文保存到文件... ---")
        with open(file_path, 'w', encoding='utf-8') as f:
            # indent=4 使 JSON 文件格式化，更易于阅读
            # ensure_ascii=False 确保多语言字符（如中文、日文、表情符号）能被正确写入
            json.dump(tweets_data, f, ensure_ascii=False, indent=4)
        
        print(f"保存成功！文件路径: {file_path}")
        return file_path

    except Exception as e:
        print(f"错误：保存文件失败。原因: {e}")
        return ""


def generate_analysis_prompt(
    markets_list: List[str],
    tweets_filepath: str
) -> str:
    """
    根据给定的市场和推文数据文件，生成一个完整且高度结构化的 Prompt。
    这个版本将详细的输出JSON格式硬编码在函数内部。

    Args:
        markets_list (List[str]): 需要分析的金融市场列表。
        tweets_filepath (str): 存储待分析推文的 JSON 文件的路径。

    Returns:
        str: 一个可以直接发送给 LLM 的、完整填充的 Prompt 字符串。
             如果文件读取失败，则返回空字符串。
    """
    # 步骤 1: 从文件路径读取推文数据
    try:
        with open(tweets_filepath, 'r', encoding='utf-8') as f:
            tweets_data = json.load(f)
        tweets_json_str = json.dumps(tweets_data, indent=2, ensure_ascii=False)
    except Exception as e:
        print(f"读取或处理推文文件时出错: {e}")
        return ""

    # 步骤 2: 格式化市场列表为字符串
    markets_list_str = ", ".join(f'"{m}"' for m in markets_list)

    # 步骤 3: 定义最终的、详细的输出格式
    output_format_description = """
Your output must be a single JSON object with one root key: "analysis_results".
This key holds a list of JSON objects, where each object represents the analysis for a single tweet.

The schema for EACH object in the "analysis_results" list is as follows:

```json
{
  "tweet_id": "string",
  "author_username": "string",
  "tweet_text": "string",
  "market_impact": [
    {
      "market": "string",
      "sentiment": "string",
      "reasoning": "string"
    }
  ],
  "black_swan_alert": {
    "is_alert": "boolean",
    "category": "string_or_null",
    "severity": "string_or_null",
    "alert_reason": "string_or_null"
  }
}
```

### Field Explanations and Constraints:

  - `tweet_id`, `author_username`, `tweet_text`: Must be copied directly from the input data for the corresponding tweet.
  - `market_impact`: A list containing an analysis object for EACH market specified in the instructions.
      - `sentiment`: Must be one of the following strings: "利好" (Bullish), "利空" (Bearish), or "中性" (Neutral).
      - `reasoning`: A concise explanation for the sentiment, **written in Simplified Chinese**.
  - `black_swan_alert`:
      - `is_alert`: `true` if a signal is detected, otherwise `false`.

      - `category`: If `is_alert` is `true`, must be one of: "Major Military Conflict", "Nuclear Proliferation/Threat", "Sovereign Debt Default", "Major Terrorist Attack", "Public Health Crisis", "Critical Infrastructure Disruption". If `is_alert` is `false`, this field must be `null`.

      - `severity`: If `is_alert` is `true`, must be one of: "Low", "Medium", "High". If `is_alert` is `false`, this must be `null`.

      - `alert_reason`: If `is_alert` is `true`, a concise explanation of the threat, **written in Simplified Chinese**. If `is_alert` is `false`, this must be `null`.
"""

    # 步骤 4: 构建最终的 Prompt
    prompt = f"""

### ROLE

You are a top-tier geopolitical and macroeconomic strategist. Your expertise lies in analyzing unstructured data like social media posts from world leaders to forecast market trends and identify nascent global risks.

### TASK

For each tweet provided, you will perform two critical assessments:

1.  **Market Impact Analysis:** Evaluate the tweet's potential impact on each of the specified financial markets.
2.  **Black Swan Alert:** Identify if the tweet signals a potential "Black Swan" event, defined as a rare, high-impact, hard-to-predict event.

### INSTRUCTIONS

1.  Analyze the impact on the following markets: {markets_list_str}.
2.  Your final output MUST be a single, valid JSON object, with no additional text or markdown formatting.
3.  Strictly adhere to the JSON schema and field constraints provided below.
4.  **CRITICAL LANGUAGE REQUIREMENT:** The JSON keys must be in English. However, all analytical text values (`sentiment`, `reasoning`, `alert_reason`) MUST be written in **Simplified Chinese (中文)**.

### OUTPUT JSON FORMAT

{output_format_description}

### --- TWEETS DATA TO ANALYZE ---

{tweets_json_str}
"""

    return prompt


def analyze_tweets_with_gemini(
    tweets_filepath: str,
    markets_list: List[str],
    google_api_key: str,
    model_name: str = "gemini-2.5-flash"
) -> Optional[str]:
    """
    读取推文文件，构建Prompt，调用Gemini API进行分析，并返回结果。
    (此版本采用 genai.Client 的 SDK 调用方式)

    Args:
        tweets_filepath (str): 存储待分析推文的JSON文件的路径。
        markets_list (List[str]): 需要分析的金融市场列表。
        google_api_key (str): 你的 Google AI Studio API 密钥。
        model_name (str, optional): 使用的Gemini模型名称。默认为 "gemini-1.5-flash"。

    Returns:
        Optional[str]: 从Gemini API返回的、符合预设格式的JSON字符串。
                       如果过程中发生任何错误，则返回 None。
    """
    print("--- 步骤 1: 生成分析用的 Prompt ---")
    full_prompt = generate_analysis_prompt(
        markets_list=markets_list,
        tweets_filepath=tweets_filepath
    )

    if not full_prompt:
        print("错误: Prompt 生成失败，分析中止。")
        return None
    
    print("Prompt 生成成功！")
    
    try:
        print(f"--- 步骤 2: 配置并调用 Google Gemini API (模型: {model_name}) ---")
        
        # 步骤 2.1: 初始化 Client
        client = genai.Client(api_key=google_api_key)
        
        # 步骤 2.2: 设置生成配置，强制要求JSON输出
        generation_config = types.GenerateContentConfig(
            response_mime_type="application/json"
        )

        # 步骤 2.3: 调用API
        # 使用 client.models.generate_content 直接调用
        response = client.models.generate_content(
            model=f"models/{model_name}", # 模型名称需要 'models/' 前缀
            contents=full_prompt,
            config=generation_config
        )

        print("API 调用成功，已收到响应！")
        
        # 检查响应中是否有内容
        if not response.text:
            print("错误: API 返回了空的响应。可能的原因是内容安全策略拦截。")
            print("Prompt Feedback:", response.prompt_feedback)
            return None
            
        # 验证并返回结果
        json.loads(response.text)
        print("响应内容是有效的JSON格式。")
        
        return response.text

    except Exception as e:
        print(f"在与 Gemini API 交互时发生严重错误: {e}")
        return None
    
    
def save_analysis_to_file(
    analysis_json_str: Optional[str],
    source_tweets_filepath: str,
    output_dir: str = '../analysis_reports'
) -> str:
    """
    将 Gemini 模型返回的分析结果(JSON字符串)保存到本地文件中。

    Args:
        analysis_json_str (Optional[str]): 包含分析结果的JSON字符串。
        source_tweets_filepath (str): 用于生成分析的原始推文文件的路径。
        output_dir (str, optional): 用于存放输出文件的目录名。
                                    默认为 'analysis_reports'。

    Returns:
        str: 成功保存的文件路径。如果保存失败，则返回空字符串。
    """
    if not analysis_json_str:
        print("--- 分析结果为空，无需保存文件。 ---")
        return ""

    try:
        # 步骤 1: 确保输出目录存在
        os.makedirs(output_dir, exist_ok=True)

        # 步骤 2: 根据原始文件名生成报告文件名，建立关联
        base_name = os.path.basename(source_tweets_filepath)
        report_filename = base_name.replace('tweets_', 'analysis_report_')
        
        # 步骤 3: 构造完整的文件路径
        file_path = os.path.join(output_dir, report_filename)

        # 步骤 4: 将JSON字符串解析为Python对象，再美化后写入文件
        print("\n--- 正在将分析结果保存到文件... ---")
        parsed_data = json.loads(analysis_json_str)
        with open(file_path, 'w', encoding='utf-8') as f:
            json.dump(parsed_data, f, ensure_ascii=False, indent=2)
        
        print(f"分析报告保存成功！文件路径: {file_path}")
        return file_path

    except json.JSONDecodeError:
        print(f"错误: 无法解析分析结果字符串为JSON。原始字符串: '{analysis_json_str[:100]}...'")
        return ""
    except Exception as e:
        print(f"错误：保存分析文件失败。原因: {e}")
        return ""


if __name__ == '__main__':
    # TODO: Twitter 的 Bearer Token 以及 Google Gemini 的 API Key
    my_bearer_token = "YOUR_BEARER_TOKEN"
    my_google_api_key = "YOUR_GOOGLE_API_KEY"

    # TODO: 定义一个想要监控的领导人列表
    leaders_to_monitor = ["POTUS"]
    ## 举例：五常
    # leaders_to_monitor = ["MFA_China", "POTUS", "10DowningStreet", "EmmanuelMacron", "KremlinRussia_E"]

    # 调用新的批量获取函数
    all_tweets_collected = fetch_all_leaders_posts(
        leader_usernames=leaders_to_monitor,
        bearer_token=my_bearer_token,
        lookback_hours=24,
        include_replies=True,
        include_retweets=True,
        limit_per_user=5
    )

    # 保存推文数据到文件
    saved_tweets_path = save_tweets_to_file(
        tweets_data=all_tweets_collected,
        output_dir='../data'
    )
    
    # TODO: 定义需要分析的市场列表
    markets = ["US Equities (S&P 500)", "Euro/USD Exchange Rate", "Gold"]
    
    # 调用 Gemini API 进行推文分析
    analysis_result_str = analyze_tweets_with_gemini(
        tweets_filepath=saved_tweets_path,
        markets_list=markets,
        google_api_key=my_google_api_key, 
        model_name="gemini-2.5-flash"
    )

    # 保存分析报告
    saved_report_path = save_analysis_to_file(
        analysis_json_str=analysis_result_str,
        source_tweets_filepath=saved_tweets_path,
        output_dir='../analysis_reports'
    )

